声明

  一个名字(标识符)能够在C++程序里使用之前必须首先声明。也就是说,必须刻画清楚它的类型,以通知编译器这个名字所引用的哪一类的实体。这里有一些例子,展示了各种各样的声明:

    char ch;
    string s;
    int count = 1;
    const double pi = 3.1415926535897932385;
    extern int error_number;
    const char* name = "Njal";
    const char* season[] = {"spring","summer","fall","winter"};

    struct Date { int d, m, y; };
    int day(Data* p) { return p->d; }
    double sqrt(double);
    template<class T> T abs(T a) { return a < 0 ? -a : a; }

    typedef complex<short> Point;
    struct User;
    enum Beer { Carlsberg, Tuborg, Thor };
    namespace NS { int a; }

正如在这些例子中所看到的,声明能做的事情可以比简单地为一个名字关联一个类型更多一些。这些声明中的大部分同时也是定义,也就是说,它们也定义了有关的名字所引用的那个实体。对于ch,它所对应实体就是适当数量的存储,以使它能够被用做一个变量---这块存储被分配。day被定义的东西就是这里所描述的函数。常数pi被定义为具有值3.1415926535897932385。Date被定义为一个新类型。Point被定义为是类型complex< short>,所以Point也就成为complex< short>的同义词。在上面这些声明中,只有

    double sqrt(double);
    extern int error_number;
    struct User;

不是定义,也就是说,它们所引用的实体必须在其他地方定义。函数sqrt的代码(体)必须通过另外的某个声明描述,int变量error_number的存储必须由某个另外的error_number的声明去分配,必须有另外的某个对类型User的声明,定义出这个类型是什么样子。例如,

    double sqrt(double d) { /* ... */ }
    int error_number = 1;

    struct User { /* ... */ };

在一个C++程序里(关于#include的影响,见9.2.3节),每个命名实体必须有恰好一个定义。当然,它们可以有许多声明。一个实体的所有声明必须在所引用的类型上完全一致。所以,下面这个片段就包含两个错误:

    int count;
    int count;    // 错误❌:重复定义

    extern int error_number;
    extern short error_number;    // 错误❌:类型不匹配

下面这个片段则没有错误(有关extern的使用参见9.2节):

    extern int error_number;
    extern int error_number;

有些定义还为它们所定义的实体确定了一个“值”。例如,

    struct Date { int d, m, y; };
    typedef complex<short> Point;
    int day(Date* p) { return p->d; }
    const double pi = 3.1415926535897932385;

对于类型、模版、函数和常数,这种所谓的“值”是持久不变的。而对那些不是常量的数据类型,这种初始值可以在以后改变。例如

    void f()
    {
        int count = 1;
        const char* name = "Bjarne";    // name是一个指向常量的变量(5.4.1节)
        // ...
        count = 2;
        name = "Marian";
    }

在前面的定义里,只有

    char ch;
    string s;

没有描述有关的值。对于如何以及何时给变量赋以默认值的问题,请参看4.9.5节和10.4.2节的解释。任何描述了初始值的声明都是一个定义。

🔚